package org.web.base.domain;

import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.web.base.domain.exception.ResultMessageEnum;
import org.web.base.domain.helper.ServiceExceptionHelper;
import org.web.base.domain.helper.SetHelper;

import java.util.Set;

@Setter
@Getter
public abstract class ExtendQueryBase extends QueryBase {

    /**
     * 圈定 'specifiedAttributeSet' 返回属性信息范围，指定的属性信息与列定义名称保持一致。
     */
    public abstract Set<String> getColumnsSet();

    /**
     * 圈定 'sortColumnName' 返回属性信息范围，指定的属性信息与列定义名称保持一致，一般来讲，并非所有属性都可以排序，需要甄别指定。
     */
    public abstract Set<String> getSortColumnsSet();

    /**
     * 排序属性名称，是数据库表的字段名，实体类的属性名格式是驼峰命名法，用下划线连接就是数据库字段名称。
     */
    private String sortColumnName;

    /**
     * 是否是降序排序，如果不指定，默认是降序排序，即最新的记录排在最前面。
     */
    private Boolean descSort;

    @Override
    public String getCountAttributeName() {
        String countAttributeName = super.getCountAttributeName();
        // 处理空值或空白字符串的情况
        if (StringUtils.isNoneBlank(countAttributeName)) {
            // 判断是否为驼峰命名并转换为下划线命名
            if (countAttributeName.matches("^[a-z]+(?:[A-Z][a-z]*)*$")) {
                return convertCamelToSnake(countAttributeName);
            }
        }
        return countAttributeName;
    }

    @Override
    public void setCountAttributeName(String countAttributeName) {
        if (StringUtils.isNotBlank(countAttributeName)) {
            if (getColumnsSet().stream()
                    .noneMatch(element -> element.equalsIgnoreCase(countAttributeName))) {
                throw ServiceExceptionHelper.buildServiceException(ResultMessageEnum.PARAM_INVALID, "Invalid attribute name for counting records, not within the specified range, please check!");
            }
        }
        super.setCountAttributeName(countAttributeName);
    }


    /**
     * 将驼峰命名转换为下划线命名
     *
     * @param camelCase 驼峰命名的字符串
     * @return 下划线命名的字符串
     */
    private String convertCamelToSnake(String camelCase) {
        // 在小写字母与大写字母之间插入下划线并转为小写
        return camelCase.replaceAll("(?<=[a-z])(?=[A-Z])", "_").toLowerCase();
    }

    public void checkAndBuildQuery() {
        try {
            if (StringUtils.isNotEmpty(sortColumnName)) {
                if (getSortColumnsSet().stream()
                        .noneMatch(element -> element.equalsIgnoreCase(sortColumnName))) {
                    throw ServiceExceptionHelper.buildServiceException(ResultMessageEnum.PARAM_INVALID, "Invalid sort attribute name, not within the specified range, please check!");
                }
                this.setOrderByClause(sortColumnName + " " + (descSort == null ? "DESC" : (descSort ? "DESC" : "ASC")));
            }

            if (StringUtils.isNotEmpty(getDistinct())) {
                if (getColumnsSet().stream()
                        .noneMatch(element -> element.equalsIgnoreCase(getDistinct()))) {
                    throw ServiceExceptionHelper.buildServiceException(ResultMessageEnum.PARAM_INVALID, "Invalid distinct column name, not within the specified range, please check!");
                }
            }

            if (getSpecifiedAttributeSet() != null && !getSpecifiedAttributeSet().isEmpty()) {
                Set<String> missingAttributesSet = SetHelper.findMissingElements(getSpecifiedAttributeSet(), getColumnsSet());
                String missingAttributes = SetHelper.join(missingAttributesSet, ",");
                if (StringUtils.isNotEmpty(missingAttributes)) {
                    throw ServiceExceptionHelper.buildServiceException(ResultMessageEnum.PARAM_INVALID, "Invalid specified attribute names, not within the specified range, please check! Illegal attribute information: " + missingAttributes);
                }
            } else {
                setDistinct(null);
            }

            if (getNullCheckedAttributes() != null && !getNullCheckedAttributes().isEmpty()) {
                Set<String> missingAttributesSet = SetHelper.findMissingElements(getNullCheckedAttributes(), getColumnsSet());
                String missingAttributes = SetHelper.join(missingAttributesSet, ",");
                if (StringUtils.isNotEmpty(missingAttributes)) {
                    throw ServiceExceptionHelper.buildServiceException(ResultMessageEnum.PARAM_INVALID, "Invalid null checked attribute names, not within the specified range, please check! Illegal attribute information: " + missingAttributes);
                }
            }

            if (getNonNullCheckedAttributes() != null && !getNonNullCheckedAttributes().isEmpty()) {
                Set<String> missingAttributesSet = SetHelper.findMissingElements(getNonNullCheckedAttributes(), getColumnsSet());
                String missingAttributes = SetHelper.join(missingAttributesSet, ",");
                if (StringUtils.isNotEmpty(missingAttributes)) {
                    throw ServiceExceptionHelper.buildServiceException(ResultMessageEnum.PARAM_INVALID, "Invalid non-null checked attribute names, not within the specified range, please check! Illegal attribute information: " + missingAttributes);
                }
            }

            String countAttributeName = getCountAttributeName();
            if (StringUtils.isNotEmpty(countAttributeName)) {
                if (getColumnsSet().stream()
                        .noneMatch(element -> element.equalsIgnoreCase(countAttributeName))) {
                    throw ServiceExceptionHelper.buildServiceException(ResultMessageEnum.PARAM_INVALID, "Invalid attribute name for counting records, not within the specified range, please check!");
                }
            }
        } catch (IllegalArgumentException e) {
            throw e;
        } catch (Exception e) {
            // 处理其他异常，记录日志并抛出异常
            throw ServiceExceptionHelper.buildServiceException(e, ResultMessageEnum.PARAM_INVALID, "Unknown error occurred during query construction");
        }
    }

}
